//
//  Sequencer.m
//  CocoaSynth
//
//  Created by Jérémy Brault on 06/02/10.
//  Copyright 2010 None. All rights reserved.
//

#import "Sequencer.h"
#import "Module.h"
#import "System.h"

@implementation Sequencer

#pragma mark -
#pragma mark Accessors

@synthesize isStarted, modules, operationQueue, removableModule;

#pragma mark -
#pragma mark Memory Management

- (id)init 
{
	if (self = [super init]) {
		isStarted = YES;
		modules = [[NSMutableSet alloc] init];
		operationQueue = [[NSOperationQueue alloc] init];
		
		removableModule = [[NSMutableSet alloc] init];
	}
	
	return self;
}

- (void)dealloc 
{
	[modules release];
	modules = nil;
	
	[operationQueue release];
	operationQueue = nil;
	
	[super dealloc];
}

#pragma mark -
#pragma mark SequencerProtocol

- (void)start 
{
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
	
	while (isStarted) {
		Module* module;
		NSInvocationOperation* readOperation = nil;
		NSInvocationOperation* computeOperation = nil;
		
		for (Module* module in removableModule) {
			[modules removeObject:module];
		}
		[removableModule removeAllObjects];
		
		for (module in modules) {
			// Reading phase
			readOperation = [[NSInvocationOperation alloc] initWithTarget:module selector:@selector(read) object:nil];
			[operationQueue addOperation:readOperation];
			
			// Computing phase
			computeOperation = [[NSInvocationOperation alloc] initWithTarget:module selector:@selector(compute) object:nil];
			[computeOperation addDependency:readOperation];
			[operationQueue addOperation:computeOperation];
			
			[readOperation release];
			readOperation = nil;
			[computeOperation release];
			computeOperation = nil;
		}
		
		System* s = [System sharedSystem];
		float time = [s sampleDuration];
		[NSThread sleepForTimeInterval:time];
		[operationQueue waitUntilAllOperationsAreFinished];
		[s newSampleProduced];
	}
	
	[pool drain];
}											  

- (void)stop 
{
	isStarted = NO;
	for (Module* module in modules) {
		[module stop];
	}
}

- (void)addModule:(Module*)module 
{
	[modules addObject:module];
}

- (void)removeModule:(Module*)module 
{
	[removableModule addObject:module];
}

@end
